package com.free.bsf.eureka.client.limitrate;

import com.free.bsf.core.util.ContextUtils;
import com.free.bsf.eureka.client.limitrate.provider.ProviderFactory;
import lombok.val;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
@Slf4j
public class LimitRateAop {

    @Pointcut("@within(org.springframework.stereotype.Controller) " +
            "|| @within(org.springframework.web.bind.annotation.RestController)")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
       if(LimitRateProperties.getLimitRateEnabled()) {
            LimitRateProperties limitRateProperties=ContextUtils.getBean(LimitRateProperties.class, true);  
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            String className = joinPoint.getTarget().getClass().getName().replaceAll("\\$\\$.*", "");
            Method method = getDeclaredMethodFor( joinPoint.getTarget().getClass(), signature.getName(),
                signature.getMethod().getParameterTypes());
            if (method == null) {
                throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName());
            }
            val methodName = method.getName();
            log.debug("限流程序触发:{}.{}",className,methodName);
            val r = limitRateProperties.parse(className,methodName);
            if(r!=null) {
                val key = r.parseKey(className,methodName);
                if(!ProviderFactory.create(r.provider).add(key,r)){
                    throw new LimitRateException("方法:"+className+"."+methodName+"命中限流规则,method:"+r.getMethod());
                }
            }
           }
        return joinPoint.proceed();
    }
    private Method getDeclaredMethodFor(Class<?> clazz, String name, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                return getDeclaredMethodFor(superClass, name, parameterTypes);
            }
        }
        return null;
    }
}
